﻿Shader "Cloud"
{
    Properties
    {
        [HDR]_ColorTint("色调", Color) = (1,1,1,1)
        [HDR]_AddColor("附加色调", Color) = (1,1,1,1)
        _AddRange("颜色范围", Range(-1, 1)) = -1
        _ScaleAndMove("缩放与移动", Vector) = (1,1,1,1)
        _Soft("接触点软边", Range(0, 1)) = 0.5
        _Edge("接触点边缘", Range(0, 0.9)) = 0.5
        _SoftEdge("边缘软边", Range(0, 0.9)) = 0.5
        [NoScaleOffset]_Control("消散", 2D) = "white" {}  
        [Header(Parallax)]
        [Space]
        [Toggle(_UseParallax)] _UseWeight("使用视差?", float) = 0
        _Parallax("视差强度", Range(0, 100)) = 0.3
        _Accuracy("视差精度", Range(4, 1000)) = 20
        _HeightAmount("视差整体高度", range(-2, 2)) = 1
    }

    SubShader
    {
        Tags { "Queue"="Transparent" "RenderType"="Opaque" }

        Pass
        {
			Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM

            #pragma multi_compile_local _ _UseParallax

            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct app_data
            {
                half4 positionOS : POSITION;
                half3 normalOS : NORMAL;
                half4 tangentOS : TANGENT;
                half4 texcoord0 : TEXCOORD0;
            };

            struct v2f
            { 
                half4 positionCS : SV_POSITION;       
                half3 normalWS : TEXCOORD0;
                half3 tangentWS : TEXCOORD1;
                half3 bitangentWS : TEXCOORD2;
                half4 uv0 : TEXCOORD3;
                half4 proj : TEXCOORD4;
                half3 viewDir : TEXCOORD5;
            };

            half4 _ColorTint;
            half4 _ScaleAndMove;
            half _Soft;
            half _Edge;
            half _SoftEdge;
            sampler2D _Control;

            half _Parallax;
            half _Accuracy;
            half _HeightAmount;

            half4 _AddColor;
            half _AddRange;

            sampler2D _CameraDepthTexture;

			v2f vert(app_data IN)
            {
                v2f o = (v2f)0;

                // 将法线从模型空间变换到世界空间
                half3 normalWS = UnityObjectToWorldNormal(IN.normalOS);

                // 将切线从模型空间变换到世界空间
                half3 tangentWS = UnityObjectToWorldDir(IN.tangentOS.xyz);

                // 计算副切线的朝向:(step(0, IN.tangentOS.w) * 2 - 1) = (IN.tangentOS.w > 0.0 ? 1.0 : -1.0)
                half crossSign = (step(0, IN.tangentOS.w) * 2 - 1) * unity_WorldTransformParams.w;

                // 副切线
                half3 bitangentWS = cross(normalWS, tangentWS) * crossSign; 

                o.tangentWS = tangentWS.xyz;
                o.bitangentWS = bitangentWS.xyz;
                o.normalWS = normalWS.xyz;

                // 计算纹理采样时使用的UV
                o.uv0.xy = IN.texcoord0.xy;
                o.uv0.zw = IN.texcoord0.zw;

                // 将顶点坐标从世界空间变换到齐次裁剪空间
                o.positionCS = UnityObjectToClipPos(IN.positionOS);
                o.proj = ComputeScreenPos(o.positionCS); 

                float3 viewDir = normalize(WorldSpaceViewDir(IN.positionOS));
                o.viewDir = viewDir;
                
                return o;
            }

            inline float Unity_SimpleNoise_RandomValue_float(float2 uv)
            {
                return frac(sin(dot(uv, float2(12.9898, 78.233))));
            }

            inline float Unity_SimpleNnoise_Interpolate_float(float a, float b, float t)
            {
                return (1.0 - t) * a + (t * b);
            }

            inline float Unity_SimpleNoise_ValueNoise_float(float2 uv)
            {
                float2 i = floor(uv);
                float2 f = frac(uv);
                f = f * f * (3.0 - 2.0 * f);

                uv = abs(frac(uv) - 0.5);
                float2 c0 = i + float2(0.0, 0.0);
                float2 c1 = i + float2(1.0, 0.0);
                float2 c2 = i + float2(0.0, 1.0);
                float2 c3 = i + float2(1.0, 1.0);
                float r0 = Unity_SimpleNoise_RandomValue_float(c0);
                float r1 = Unity_SimpleNoise_RandomValue_float(c1);
                float r2 = Unity_SimpleNoise_RandomValue_float(c2);
                float r3 = Unity_SimpleNoise_RandomValue_float(c3);

                float bottomOfGrid = Unity_SimpleNnoise_Interpolate_float(r0, r1, f.x);
                float topOfGrid = Unity_SimpleNnoise_Interpolate_float(r2, r3, f.x);
                float t = Unity_SimpleNnoise_Interpolate_float(bottomOfGrid, topOfGrid, f.y);
                return t;
            }

            float Unity_SimpleNoise_float(float2 UV)
            {
                float t = 0.0;

                float freq = pow(2.0, float(0));
                float amp = pow(0.5, float(3 - 0));
                t += Unity_SimpleNoise_ValueNoise_float(float2(UV.x / freq, UV.y / freq)) * amp;

                freq = pow(2.0, float(1));
                amp = pow(0.5, float(3 - 1));
                t += Unity_SimpleNoise_ValueNoise_float(float2(UV.x / freq, UV.y / freq)) * amp;

                freq = pow(2.0, float(2));
                amp = pow(0.5, float(3 - 2));
                t += Unity_SimpleNoise_ValueNoise_float(float2(UV.x / freq, UV.y / freq)) * amp;

                return t;
            }

            float NoiseParallax(half2 uv)
            {
                return Unity_SimpleNoise_float(uv + _ScaleAndMove.zw * _Time.y) * Unity_SimpleNoise_float(uv * 0.6 + _ScaleAndMove.zw * 0.8 * _Time.y);
            }
            
            // 获取视差贴图的高度数据
            inline half ParallaxTexture(half2 uv)
            {
                return NoiseParallax(uv) + _HeightAmount;
            }

            // viewDirection:视角方向 parallax:视差强度 
            // uv:纹理坐标 numLayers:细分的层数
            // return:纹理坐标的偏移
            inline half2 ParallaxUvDelta(
                half3 viewDirection, half parallax,
                half2 uv, half numLayers)
            {
                half3 viewDir = normalize(viewDirection);

                // 单层步逼进的高度
                half layerHeight = 1.0 / numLayers;

                // 最高的高度值, 下面将取到推算高度
                half currentLayerHeight = 1.0;

                // delta 最大值
                half2 P = viewDir.xy * parallax;

                // delta 单步逼近值
                half2 deltaTexCoords = P / numLayers;

                // 纹理在平面中步步偏移的坐标
                half2 currentTexCoords = uv;

                // 高度图中的高度
                half currentDepthMapValue = ParallaxTexture(currentTexCoords);

                // 开始一步步逼近，直到找到平面与视线的交点
                while (currentLayerHeight > currentDepthMapValue)
                {
                    // 对纹理坐标进行偏移
                    currentTexCoords -= deltaTexCoords;

                    // 重新获取高度图中的高度，当高度比平面与视线交点大时，则条件满足跳出循环
                    currentDepthMapValue = ParallaxTexture(currentTexCoords);

                    // 高度下降一级，在新的推算高度上继续尝试
                    currentLayerHeight -= layerHeight;
                }

                // 往前回退一个点
                half2 prevTexCoords = currentTexCoords + deltaTexCoords;

                // 当前取到的点，高度图实际高度与推算高度相差
                half afterHeight = currentDepthMapValue - currentLayerHeight;

                // currentLayerHeight + layerHeight:上一个点的推算高度
                // 上一个点的高度图实际高度
                // 上一个点，推算高度与高度图实际高度相差
                half beforeHeight = currentLayerHeight + layerHeight - ParallaxTexture(prevTexCoords);

                // 利用 afterHeight 与 beforeHeight 得到权重，在两个平面与视线的交点间使用权重进行差值
                half weight = afterHeight / (afterHeight + beforeHeight);

                // 根据权重得到估算出的纹理偏移
                half2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight);

                return finalTexCoords - uv;
            }

            // 使用SV_Target，用来标记把渲染结果存入RT0
            half4 frag(v2f IN) : SV_Target
            {
                // 切线空间转世界空间矩阵
                half3x3 tangentToWorld = half3x3(
                    normalize(IN.tangentWS.xyz),
                    normalize(IN.bitangentWS.xyz),
                    normalize(IN.normalWS.xyz));

                // 世界坐标观察方向:世界摄像机坐标 - 世界片段坐标       
                half3 viewDir = normalize(IN.viewDir);

                // 视差偏移量
                half2 mainUV = IN.uv0.xy * _ScaleAndMove.xy;

            #ifdef _UseParallax
                half2 uvDelta = ParallaxUvDelta(mul(tangentToWorld, viewDir), _Parallax, mainUV, _Accuracy);
            #else
                half2 uvDelta = 0;
            #endif

                // 视差计算后的纹理坐标
                half2 uv = mainUV + uvDelta;

                // 附加颜色
                half4 addColor = _AddColor;
                half height = saturate((1 - NoiseParallax(uv)) + _AddRange);

                half4 baseColor = _ColorTint;
                baseColor.rgb = lerp(baseColor.rgb, addColor.rgb, height);

                float2 depthUV = IN.proj.xy / IN.proj.w;

                half depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, depthUV);
                depth = LinearEyeDepth(depth);

                half4 ControlColor = tex2D(_Control, IN.uv0.xy);
  
                half deltaDepth = depth - IN.proj.w;
                baseColor.a = saturate(deltaDepth * _Soft * (1 - height) - (1 - ControlColor.r));
                baseColor.a = smoothstep(_Edge, 1, baseColor.a);

                baseColor.a *= smoothstep(0, _SoftEdge, IN.uv0.y);
                baseColor.a *= smoothstep(0, _SoftEdge, 1 - IN.uv0.y);
                baseColor.a *= smoothstep(0, _SoftEdge, IN.uv0.x);
                baseColor.a *= smoothstep(0, _SoftEdge, 1 - IN.uv0.x);

                baseColor.a *= lerp(_ColorTint.a, _AddColor.a, height);

                return baseColor;
            }
            ENDCG
        }
    }
}